// ------------------------------------------------------------------------------------------
// Licensed by Interprise Solutions.
// http://www.InterpriseSolutions.com
// For details on this license please visit  the product homepage at the URL above.
// THE ABOVE NOTICE MUST REMAIN INTACT.
// ------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Data;
using System.Data.SqlClient;

namespace InterpriseSuiteEcommerceCommon
{
    public class CreditCardValidator
    {
        private CardTypes _cardTypes;

        [Flags, Serializable]
        public enum CardTypes
        {
            MasterCard = 0x0001,
            VISA = 0x0002,
            Amex = 0x0004,
            Diners = 0x0008,
            enRoute = 0x0010,
            Discover = 0x0020,
            JCB = 0x0040,
            Unknown = 0x0080,
            BankCard = 0x0080,
            CUP = 0x0080,
            Laser = 0x0080,
            Maestro = 0x0080,
            MaestroInt = 0x0080,
            MasterCardPurchase = 0x0080,
            DinersClub = 0x0008,
            Solo = 0x0080,
            Switch = 0x0080,
            VisaElectron = 0x0080,
            VisaPurchase = 0x0080,
            XYZ = 0x0040,
            All = CardTypes.Amex | CardTypes.DinersClub |
                             CardTypes.Discover | CardTypes.Discover |
                             CardTypes.enRoute | CardTypes.JCB |
                             CardTypes.MasterCard | CardTypes.VISA |
                             CardTypes.BankCard | CardTypes.Laser |
                             CardTypes.Maestro | CardTypes.MaestroInt |
                             CardTypes.MasterCardPurchase | CardTypes.Solo |
                             CardTypes.Switch | CardTypes.VisaElectron |
                             CardTypes.VisaPurchase | CardTypes.XYZ
        }

        public string AcceptedCardTypes
        {
            get
            {
                return _cardTypes.ToString();
            }
            set
            {
                try
                {
                    _cardTypes = (CardTypes)Enum.Parse(typeof(CardTypes), value, true);
                }
                catch{}
            }
        }

        public bool ValidateCardNumber(string cardNumber)
        {
            try
            {
                // Array to contain individual numbers
                System.Collections.ArrayList CheckNumbers = new ArrayList();
                // So, get length of card
                int CardLength = cardNumber.Length;

                // Double the value of alternate digits, starting with the second digit
                // from the right, i.e. back to front.
                // Loop through starting at the end
                for (int i = CardLength - 2; i >= 0; i = i - 2)
                {
                    // Now read the contents at each index, this
                    // can then be stored as an array of integers

                    // Double the number returned
                    CheckNumbers.Add(Int32.Parse(cardNumber[i].ToString()) * 2);
                }

                int CheckSum = 0;    // Will hold the total sum of all checksum digits

                // Second stage, add separate digits of all products
                for (int iCount = 0; iCount <= CheckNumbers.Count - 1; iCount++)
                {
                    int _count = 0;    // will hold the sum of the digits

                    // determine if current number has more than one digit
                    if ((int)CheckNumbers[iCount] > 9)
                    {
                        int _numLength = ((int)CheckNumbers[iCount]).ToString().Length;
                        // add count to each digit
                        for (int x = 0; x < _numLength; x++)
                        {
                            _count = _count + Int32.Parse(
                                  ((int)CheckNumbers[iCount]).ToString()[x].ToString());
                        }
                    }
                    else
                    {
                        // single digit, just add it by itself
                        _count = (int)CheckNumbers[iCount];
                    }
                    CheckSum = CheckSum + _count;    // add sum to the total sum
                }
                // Stage 3, add the unaffected digits
                // Add all the digits that we didn't double still starting from the
                // right but this time we'll start from the rightmost number with 
                // alternating digits
                int OriginalSum = 0;
                for (int y = CardLength - 1; y >= 0; y = y - 2)
                {
                    OriginalSum = OriginalSum + Int32.Parse(cardNumber[y].ToString());
                }

                // Perform the final calculation, if the sum Mod 10 results in 0 then
                // it's valid, otherwise its false.
                return (((OriginalSum + CheckSum) % 10) == 0);
            }
            catch
            {
                return false;
            }
        }

        public bool IsValidCardType(string cardNumber)
        {
            // AMEX -- 34 or 37 -- 15 length
            if ((Regex.IsMatch(cardNumber, "^(34|37)"))
                 && ((_cardTypes & CardTypes.Amex) != 0))
                return (15 == cardNumber.Length);

            // MasterCard -- 51 through 55 -- 16 length
            else if ((Regex.IsMatch(cardNumber, "^(51|52|53|54|55)")) &&
                      ((_cardTypes & CardTypes.MasterCard) != 0))
                return (16 == cardNumber.Length);

            // VISA -- 4 -- 13 and 16 length
            else if ((Regex.IsMatch(cardNumber, "^(4)")) &&
                      ((_cardTypes & CardTypes.VISA) != 0))
                return (13 == cardNumber.Length || 16 == cardNumber.Length);

            // Diners Club -- 300-305, 36 or 38 -- 14 length
            else if ((Regex.IsMatch(cardNumber, "^(300|301|302|303|304|305|36|38)")) &&
                      ((_cardTypes & CardTypes.DinersClub) != 0))
                return (14 == cardNumber.Length);

            // enRoute -- 2014,2149 -- 15 length
            else if ((Regex.IsMatch(cardNumber, "^(2014|2149)")) &&
                      ((_cardTypes & CardTypes.DinersClub) != 0))
                return (15 == cardNumber.Length);

            // Discover -- 6011 -- 16 length
            else if ((Regex.IsMatch(cardNumber, "^(6011)")) &&
                     ((_cardTypes & CardTypes.Discover) != 0))
                return (16 == cardNumber.Length);

            // JCB -- 3 -- 16 length
            else if ((Regex.IsMatch(cardNumber, "^(3)")) &&
                     ((_cardTypes & CardTypes.JCB) != 0))
                return (16 == cardNumber.Length);

            // JCB -- 2131, 1800 -- 15 length
            else if ((Regex.IsMatch(cardNumber, "^(2131|1800)")) &&
                       ((_cardTypes & CardTypes.JCB) != 0))
                return (15 == cardNumber.Length);
            else
            {
                 //Card type wasn't recognised, provided Unknown is in the 
                 //CardTypes property, then return true, otherwise return false.
                if ((_cardTypes & CardTypes.Unknown) != 0)
                    return true;
                else
                    return false;
            }
        }

        public bool IsValidExpirationDate(string expirationDate)
        {
            bool valid = false;
            string strCurrentMonthYear = DateTime.Now.ToString("yyyyMM");
            int intCurrentMonthYear = int.Parse(strCurrentMonthYear);
            int intExpirationDate = 0;
            if (int.TryParse(expirationDate, out intExpirationDate))
            {
                if (intCurrentMonthYear <= intExpirationDate)
                    valid = true;
            }
            return valid;
        }

    }
}
